#ifndef PW_PBL_TLSF_STRING_HPP_
/// TLSF 动态内存管理器优化string实现
#define PW_PBL_TLSF_STRING_HPP_

#include "../TLSF.hpp"
#include <string>
#include <memory>
#include <limits>

namespace pw_pbl::TLSF
{
	template <typename CHAR, typename TRAITS = std::char_traits<CHAR>, typename TP = config::TDefaultParms>
	requires std::is_pod_v<CHAR>
	class basic_string
	{
	public:
		using traits_type = TRAITS;
		using value_type = CHAR;
		using allocator_type = Allocator<value_type, TP>;
		using size_type = typename std::allocator_traits<allocator_type>::size_type;
		using difference_type = typename std::allocator_traits<allocator_type>::difference_type;
		using reference = value_type &;
		using const_reference = const value_type &;
		using pointer = typename std::allocator_traits<allocator_type>::pointer;
		using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
		using iterator = pointer;
		using const_iterator = const_pointer;
		using reverse_iterator = std::reverse_iterator<iterator>;
		using const_reverse_iterator = std::reverse_iterator<const_iterator>;
		using string_view = std::basic_string_view<value_type, traits_type>;

		template <typename... P>
		static constexpr bool fStringViewLike = requires(P &&...args) { string_view{std::forward<P>(args)...}; };

		static constexpr auto npos = std::numeric_limits<size_type>::max();

	private:
		value_type *_pData;
		size_type _size;
		size_type _capacity;

	protected:
		inline constexpr std::unique_ptr<value_type, Deleter<value_type, TP>> _setCapacity(size_type cap) noexcept
		{
			_capacity = cap;
			auto [pNew, pOld] = get_allocator().ReAlloc(_pData, _capacity);
			_pData = pNew;
			if (!_pData)
				_capacity = _size = 0;
			return std::move(pOld);
		}

		constexpr pointer _appendAlloc(size_type cnt) noexcept
		{
			auto osize = _size;
			_size += cnt;
			if (_size > _capacity)
			{
				reserve(_size);
				if (!_pData)
					return nullptr;
			}
			return &_pData[osize];
		}

		constexpr pointer _insertAlloc(size_type index, size_type cnt) noexcept
		{
			if (index > _size)
				return nullptr;
			if (index == _size)
				return _appendAlloc(cnt);
			auto osize = _size;
			_size += cnt;
			if (_size > _capacity)
			{
				auto pOld = _setCapacity(_size);
				if (!_pData)
					return nullptr;
				if (pOld)
				{
					traits_type::copy(_pData, pOld.get(), index);
					std::copy_backward(pOld.get() + index, pOld.get() + osize, _pData + _size);
				}
				else
					std::copy_backward(_pData + index, _pData + osize, _pData + _size);
				return _pData + index;
			}
			else
			{
				std::copy_backward(_pData + index, _pData + osize, _pData + _size);
				return _pData + index;
			}
		}

		constexpr pointer _sliceAlloc(size_type pos, size_type count, size_type newcnt) noexcept
		{
			if (pos >= _size || _size - pos < count)
			{
				if (pos == _size)
					return count == 0 ? _appendAlloc(newcnt) : nullptr;
				return nullptr;
			}
			if (newcnt == count)
			{
			}
			else if (newcnt < count)
			{
				traits_type::copy(_pData + pos + newcnt, _pData + pos + count, _size - pos - count);
				_size -= count - newcnt;
			}
			else if (auto nsize = _size + newcnt - count; nsize <= _capacity)
			{
				std::copy_backward(_pData + pos + count, _pData + _size, _pData + nsize);
				_size = nsize;
			}
			else
			{
				auto pOld = _setCapacity(nsize);
				if (!_pData)
					return nullptr;
				if (pOld)
				{
					traits_type::copy(_pData, pOld.get(), pos);
					traits_type::copy(_pData + pos + newcnt, pOld.get() + pos + count, _size - pos - count);
					_size = nsize;
				}
				else
				{
					std::copy_backward(_pData + pos + count, _pData + _size, _pData + nsize);
					_size = nsize;
				}
			}
			return _pData + pos;
		}

	public:
		constexpr ~basic_string() noexcept { get_allocator().deallocate(_pData, _capacity); }

		constexpr basic_string() noexcept : _pData(nullptr), _size(0), _capacity(0) {}

		explicit constexpr basic_string(size_type size, size_type capacity) noexcept
		{
			capacity = std::max(size, capacity);
			_pData = get_allocator().allocate(capacity);
			if (_pData)
			{
				_size = size;
				_capacity = capacity;
			}
			else
				_capacity = _size = 0;
		}

		constexpr basic_string(basic_string &&other) noexcept : _pData(std::exchange(other._pData, nullptr)), _size(other._size), _capacity(other._capacity) {}
		constexpr basic_string(const basic_string &other) noexcept : _pData(get_allocator().allocate(other.size())), _size(_pData ? other._size : 0), _capacity(_size)
		{
			std::copy_n(other._pData, _size, _pData);
		}

		template <typename... P>
		requires fStringViewLike<P...>
		constexpr basic_string(P &&...args) noexcept { assign(string_view{std::forward<P>(args)...}); }

		template <typename P>
		requires fStringViewLike<P>
		constexpr basic_string &operator=(P &&args) noexcept { return assign(string_view{std::forward<P>(args)}); }

		constexpr basic_string &operator=(const basic_string &other) noexcept { return assign(other); }
		constexpr basic_string &operator=(basic_string &&other) noexcept
		{
			get_allocator().deallocate(_pData, _capacity);
			_pData = std::exchange(other._pData, nullptr);
			_size = other._size;
			_capacity = other._capacity;
			return *this;
		}

		template <typename NTRAITS>
		constexpr basic_string &assign(std::basic_string_view<value_type, NTRAITS> view) noexcept
		{
			auto nsize = view.size();
			if (_capacity < nsize)
			{
				_setCapacity(nsize);
				if (!_pData)
					return *this;
			}
			_size = nsize;
			traits_type::move(_pData, view.data(), _size);
			return *this;
		}

		template <typename... P>
		requires fStringViewLike<P...>
		constexpr basic_string &assign(P &&...args) noexcept { return assign(string_view{std::forward<P>(args)...}); }

		constexpr allocator_type get_allocator() const noexcept { return {}; }

	public:
		constexpr reference operator[](size_type i) noexcept { return _pData[i]; }
		constexpr const_reference operator[](size_type i) const noexcept { return _pData[i]; }
		constexpr reference front() noexcept { return _pData[0]; }
		constexpr const_reference front() const noexcept { return _pData[0]; }
		constexpr reference back() noexcept { return *(_pData + (_size - 1)); }
		constexpr const_reference back() const noexcept { return *(_pData + (_size - 1)); }
		constexpr pointer data() noexcept { return _pData; }
		constexpr const_pointer data() const noexcept { return _pData; }
		constexpr const_pointer c_str() noexcept
		{
			if (_size == _capacity)
			{
				reserve(_capacity + 1);
				if (!_pData)
					return nullptr;
			}
			_pData[_size] = '\0';
			return _pData;
		}
		constexpr operator std::basic_string_view<value_type, traits_type>() const noexcept { return {_pData, _size}; }

	public:
		constexpr iterator begin() noexcept { return &_pData[0]; }
		constexpr const_iterator begin() const noexcept { return &_pData[0]; }
		constexpr const_iterator cbegin() const noexcept { return &_pData[0]; }
		constexpr iterator end() noexcept { return &_pData[_size]; }
		constexpr const_iterator end() const noexcept { return &_pData[_size]; }
		constexpr const_iterator cend() const noexcept { return &_pData[_size]; }

		constexpr iterator rbegin() noexcept { return &_pData[-1]; }
		constexpr const_iterator rbegin() const noexcept { return &_pData[-1]; }
		constexpr const_iterator crbegin() const noexcept { return &_pData[-1]; }
		constexpr iterator rend() noexcept { return &_pData[_size - 1]; }
		constexpr const_iterator rend() const noexcept { return &_pData[_size - 1]; }
		constexpr const_iterator crend() const noexcept { return &_pData[_size - 1]; }

	public:
		[[nodiscard]] constexpr bool empty() const noexcept { return _size == 0; }
		constexpr size_type size() const noexcept { return _size; }
		constexpr size_type length() const noexcept { return _size; }
		constexpr size_type max_size() const noexcept { return TImp<TP>::EMaxAllocableSize / sizeof(value_type); }
		constexpr void reserve(size_type cap) noexcept
		{
			if (cap <= _capacity)
				return;
			auto pOld = _setCapacity(cap);
			if (!_pData)
				return;
			if (pOld)
				traits_type::copy(_pData, pOld.get(), _size);
		}
		constexpr size_type capacity() const noexcept { return _capacity; }
		constexpr void shrink_to_fit() noexcept
		{
			if (_size >= _capacity)
				return;
			auto pOld = _setCapacity(_size);
			if (!_pData)
				return;
			if (pOld)
				traits_type::copy(_pData, pOld.get(), _size);
		}

	public:
		constexpr void clear() noexcept { _size = 0; }
		constexpr basic_string &insert(size_type index, size_type count, value_type ch) noexcept
		{
			auto pt = _insertAlloc(index, count);
			if (pt)
				std::fill_n(pt, count, ch);
			return *this;
		}
		template <typename... P>
		requires fStringViewLike<P...>
		constexpr basic_string &insert(size_type index, P &&...args) noexcept
		{
			auto view = string_view{std::forward<P>(args)...};
			if (view.empty())
				return *this;
			auto pt = _insertAlloc(index, view.size());
			if (pt)
				traits_type::copy(pt, view.data(), view.size());
			return *this;
		}
		constexpr void push_back(value_type ch) noexcept
		{
			auto pt = _appendAlloc(1);
			if (pt)
				*pt = ch;
		}
		constexpr void pop_back() noexcept { _size -= 1; }

		template <typename... P>
		requires fStringViewLike<P...>
		constexpr basic_string &append(P &&...args) noexcept
		{
			auto view = string_view{std::forward<P>(args)...};
			if (view.empty())
				return *this;
			auto pt = _appendAlloc(view.size());
			if (pt)
				traits_type::copy(pt, view.data(), view.size());
			return *this;
		}
		constexpr basic_string &replace(size_type pos, size_type count, const_pointer ipData, size_type isize) noexcept
		{
			auto pt = _sliceAlloc(pos, count, isize);
			if (pt)
				traits_type::copy(pt, ipData, isize);
			return *this;
		}
		constexpr basic_string substr(size_type pos = 0, size_type count = npos) const
		{
			if (pos >= _size)
				return {};
			auto n = _size - pos;
			return string_view{&_pData[pos], std::min(n, count)};
		}
		constexpr void resize(size_type count) noexcept
		{
			if (count <= _size)
				_size = count;
			else
			{
				reserve(count);
				if (_pData)
				{
					std::fill_n(_pData + _size, count - _size, value_type{});
					_size = count;
				}
			}
		}

		template <class Operation>
		constexpr void resize_and_overwrite(size_type count, Operation op) noexcept
		{
			if (count <= _capacity)
				_size = std::move(op)(_pData, count);
			else
			{
				reserve(count);
				if (_pData)
					_size = std::move(op)(_pData, count);
			}
		}
		constexpr void resize_for_overwrite(size_type size, size_type capacity) noexcept
		{
			if (size < _size)
			{
				_size = size;
				reserve(capacity);
			}
			else
			{
				reserve(std::max(size, capacity));
				if (_pData)
					_size = size;
			}
		}
	};

	extern template class basic_string<char>;
} // namespace pw_pbl::TLSF

#endif // PW_PBL_TLSF_STRING_HPP_
